package org.bot.plugin.web.util;

import lombok.val;
import org.bot.api.flow.FlowNode;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * @author 沧月
 * @create 0011 2023/9/11 13:48
 * @description <Web网页操作API>
 */

@Component
public class WebApi {

    /**
     * @author: 沧月
     * @date: 0011 2023/9/11 13:50
     * @param: [webDriver, url]
     * @return: void
     * @description: 在一个标签页打开一个地址
     **/
    public void goTo(WebDriver webDriver, String url) {
        webDriver.get(url);
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 17:21
     * @param: [note]
     * @return: org.openqa.selenium.WebDriver
     * @description: 获取WebDriver对象
     **/
    public WebDriver getWebDriver(FlowNode note) {
        if (note.getDriveWeb() == null) {
            throw new RuntimeException(String.format("节点【%s】未指定对应窗口对象", note.getName()));
        }
        return (WebDriver) note.getDriveWeb();
    }

    /**
     * @author: 沧月
     * @date: 0011 2023/9/11 13:52
     * @param: [webDriver, url]
     * @return: void
     * @description: 创建一个新的tab页，并且打开一个地址
     **/
    public void newPage(WebDriver webDriver, String url) {
        ((JavascriptExecutor) webDriver).executeScript("window.open('', '_blank');");
        final Object[] objects = webDriver.getWindowHandles().toArray();
        webDriver.switchTo().window(objects[objects.length - 1].toString());
        webDriver.get(url);
    }

    public void waitForElementToLoad(WebDriver webDriver, int timeout, SelectorType selectorType, String cmd) {
        By by;
        switch (selectorType) {
            case ID:
                by = By.id(cmd);
                break;
            case CLASS:
                by = By.className(cmd);
                break;
            case CSS:
                //driver.findElement(By.cssSelector("[data-test='your_attribute_value']"));
                by = By.cssSelector(cmd);
                break;
            case XPATH:
                by = By.xpath(cmd);
                break;
            case NAME:
                by = By.name(cmd);
                break;
            default:
                throw new RuntimeException("没有该选择器!");
        }
        WebDriverWait wait = new WebDriverWait(webDriver, Duration.ofSeconds(timeout));
        wait.until(ExpectedConditions.presenceOfElementLocated(by));
    }


    /**
     * @author: 沧月
     * @date: 0011 2023/9/11 17:41
     * @param: [webDriver, selectorType, cmd]
     * @return: void
     * @description: 点击某个元素
     **/
    public boolean click(WebDriver webDriver, SelectorType selectorType, String cmd) {
        WebElement webElement = this.getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(webElement, cmd);
        webElement.click();
        return true;
    }

    /**
     * @author: 沧月
     * @date: 0011 2023/9/11 17:41
     * @param: [webDriver, selectorType, cmd]
     * @return: void
     * @description: 双击某个元素
     **/
    public boolean dbClick(WebDriver webDriver, SelectorType selectorType, String cmd) {
        WebElement webElement = this.getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(webElement, cmd);
        // 执行双击操作
        Actions actions = new Actions(webDriver);
        actions.doubleClick(webElement).perform();
        return true;
    }

    /**
     * @author: 沧月
     * @date: 0011 2023/9/11 17:55
     * @param: [webDriver, cmd]
     * @return: void
     * @description: 执行JavaScript 代码
     **/
    public Object script(WebDriver webDriver, String cmd, Object... arg) {
        return ((JavascriptExecutor) webDriver).executeScript(cmd, arg);
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 17:43
     * @param: [webDriver, key, value]
     * @return: void
     * @description: 设置全局value，联动JavaScript执行器执行Java内存中的数据
     **/
    public void setGlobalValue(WebDriver webDriver, String key, Object value) {
        if (value == null) {
            return;
        }
        String v = null;
        if (value instanceof String) {
            v = "'" + value + "'";
        } else {
            v = value.toString();
        }
        ((JavascriptExecutor) webDriver).executeScript("window." + key + " = " + v);
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:04
     * @param: [webDriver, selectorType, cmd]
     * @return: java.lang.String
     * @description: 获取表单里面的值
     **/
    public String getValue(WebDriver webDriver, SelectorType selectorType, String cmd) {
        WebElement webElement = this.getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(webElement, cmd);
        switch (webElement.getTagName()) {
            case "select":
            case "textarea":
                return webElement.getAttribute("value");
            case "checkbox":
            case "radio":
                return this.getCheckboxOrRadio(webDriver, selectorType, cmd);
            case "input":
                switch (webElement.getAttribute("type")) {
                    case "radio":
                    case "checkbox":
                        return this.getCheckboxOrRadio(webDriver, selectorType, cmd);
                    default:
                        return webElement.getAttribute("value");
                }
            default:
                webElement.getAttribute("value");
        }
        return null;
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 16:35
     * @param: [webDriver, selectorType, cmd, value]
     * @return: void
     * @description: 设置输入框值
     **/
    public void setValue(WebDriver webDriver, SelectorType selectorType, String cmd, Object value) {
        List<WebElement> webElements = this.getWebElements(webDriver, selectorType, cmd);
        String v = value.toString();
        webElements.forEach(webElement -> {
            if (value == null) {
                webElement.clear();
            } else {
                if ("select".equals(webElement.getTagName())) {
                    Select select = new Select(webElement);
                    select.selectByValue(v);
                } else {
                    String type = webElement.getAttribute("type");
                    if ("radio".equals(type) || "checkbox".equals(type)) {
                        if (!webElement.isSelected()) {
                            if (Arrays.asList(v.split(",")).contains(webElement.getAttribute("value"))) {
                                webElement.click();
                            }
                        }
                    } else {
                        webElement.sendKeys(v);
                    }
                }
            }
        });
    }


    /**
     * @author: 沧月
     * @date: 0028 2023/9/28 9:56
     * @param: [webDriver, selectorType, cmd]
     * @return: java.lang.String
     * @description: 获取HTML
     **/
    public String getHtml(WebDriver webDriver, SelectorType selectorType, String cmd) {
        WebElement webElement = this.getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(webElement, cmd);
        return webElement.getAttribute("innerHTML");
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:08
     * @param: [webDriver]
     * @return: void
     * @description: 后退操作
     **/
    public void back(WebDriver webDriver) {
        webDriver.navigate().back();
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:08
     * @param: [webDriver]
     * @return: void
     * @description: 前进操作
     **/
    public void forward(WebDriver webDriver) {
        webDriver.navigate().forward();
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:13
     * @param: [webDriver]
     * @return: void
     * @description: 刷新页面
     **/
    public void refresh(WebDriver webDriver) {
        webDriver.navigate().refresh();
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:10
     * @param: [millis]
     * @return: void
     * @description: 暂停多少毫秒再执行
     **/
    public void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:16
     * @param: [webDriver, selectorType, cmd]
     * @return: java.lang.String
     * @description: 获取所有标签页的句柄
     **/
    public Set<String> getAllPageId(WebDriver webDriver) {
        return webDriver.getWindowHandles();
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:19
     * @param: [webDriver]
     * @return: java.lang.String
     * @description: 获取当前标签页的句柄
     **/
    public String getCurPageId(WebDriver webDriver) {
        return webDriver.getWindowHandle();
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:20
     * @param: [webDriver]
     * @return: void
     * @description: 切换到指定标签页
     **/
    public void switchPage(WebDriver webDriver, String pageId) {
        webDriver.switchTo().window(pageId);
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:20
     * @param: [webDriver]
     * @return: void
     * @description: 切换到指定标签页
     **/
    public void switchPage(WebDriver webDriver, int pageIndex) {
        Set<String> allPageId = this.getAllPageId(webDriver);
        String pageId = allPageId.toArray(new String[]{})[pageIndex];
        this.switchPage(webDriver, pageId);
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:23
     * @param: [webDriver]
     * @return: void
     * @description: 关闭当前标签页
     **/
    public void closePage(WebDriver webDriver) {
        webDriver.close();
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 9:24
     * @param: [webDriver]
     * @return: java.lang.String
     * @description: 获取当前页面URL
     **/
    public String getCurrentUrl(WebDriver webDriver) {
        return webDriver.getCurrentUrl();
    }


    /**
     * @author: 沧月
     * @date: 0028 2023/9/28 11:28
     * @param: [webDriver, selectorType, cmd]
     * @return: java.lang.String
     * @description: 获取元素属性值
     **/
    public String getAttribute(WebDriver webDriver, SelectorType selectorType, String cmd, String attrName) {
        WebElement webElement = getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(webElement, cmd);
        return webElement.getAttribute(attrName);
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 16:21
     * @param: [webDriver]
     * @return: java.lang.String
     * @description: 获取当前页面标题
     **/
    public String getTitle(WebDriver webDriver) {
        return webDriver.getTitle();
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 16:28
     * @param: [webDriver, selectorType, cmd]
     * @return: void
     * @description: 切换frame
     **/
    public void switchFrame(WebDriver webDriver, SelectorType selectorType, String cmd) {
        WebElement webElement = getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(webElement, cmd);
        webDriver.switchTo().frame(webElement);
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 16:30
     * @param: [webDriver]
     * @return: void
     * @description: 切换回主页面
     **/
    public void switchMainFrame(WebDriver webDriver) {
        webDriver.switchTo().defaultContent();
    }

    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 16:41
     * @param: [webDriver, selectorType, cmd]
     * @return: java.lang.String
     * @description: 获取文本
     **/
    public String getText(WebDriver webDriver, SelectorType selectorType, String cmd) {
        WebElement webElement = getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(webElement, cmd);
        return webElement.getText();
    }


    /**
     * @author: 沧月
     * @date: 0012 2023/9/12 16:53
     * @param: []
     * @return: void
     * @description: 使元素获取move事件
     **/
    public void moveToElement(WebDriver webDriver, SelectorType selectorType, String cmd) {
        // 定位要执行鼠标悬停操作的元素
        WebElement elementToHover = getWebElement(webDriver, selectorType, cmd);
        ErrorCheck.checkElement(elementToHover, cmd);
        // 创建Actions对象并使用moveToElement方法模拟鼠标悬停
        Actions actions = new Actions(webDriver);
        actions.moveToElement(elementToHover).perform();
    }

    /**
     * @param webDriver
     * @param selectorType
     * @param cmd
     * @return 检查元素是否存在
     */
    public boolean checkElement(WebDriver webDriver, SelectorType selectorType, String cmd) {
        WebElement webElement = getWebElement(webDriver, selectorType, cmd);
        if (webElement == null) {
            return false;
        }
        return true;
    }


    private String getCheckboxOrRadio(WebDriver webDriver, SelectorType selectorType, String cmd) {
        List<WebElement> webElements = getWebElements(webDriver, selectorType, cmd);
        StringBuilder sb = new StringBuilder();
        for (WebElement element : webElements) {
            if (element.isSelected()) {
                if (sb.length() > 0) {
                    sb.append(",");
                }
                sb.append(element.getAttribute("value"));
            }
        }
        return sb.length() == 0 ? null : sb.toString();
    }

    private WebElement getWebElement(WebDriver webDriver, SelectorType selectorType, String cmd) {
        List<WebElement> webElements = this.getWebElements(webDriver, selectorType, cmd);
        if (webElements == null || webElements.isEmpty()) {
            return null;
        }
        return webElements.get(0);
    }

    public List<WebElement> getWebElements(WebDriver webDriver, SelectorType selectorType, String cmd) {
        List<WebElement> webElement;
        switch (selectorType) {
            case ID:
                webElement = webDriver.findElements(By.id(cmd));
                break;
            case CLASS:
                webElement = webDriver.findElements(By.className(cmd));
                break;
            case CSS:
                //driver.findElement(By.cssSelector("[data-test='your_attribute_value']"));
                webElement = webDriver.findElements(By.cssSelector(cmd));
                break;
            case XPATH:
                webElement = webDriver.findElements(By.xpath(cmd));
                break;
            case NAME:
                webElement = webDriver.findElements(By.name(cmd));
                break;
            default:
                throw new RuntimeException("没有该选择器!");
        }
        return webElement;
    }
}
